/************************************************************************************************************************************************************
*                          DESENVOLVEDORES: Denis Tobias; Diogo Oliveira; Marcel M. Marum; Marcos F. Rachid; Rodrigo M. Botelho
*                                       ORIENTADOR e DESENVOLVEDOR: Prof.Dr. Eraldo Pereira Marinho
*
*                                                           Arquivo: parser.c
*
************************************************************************************************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "parser.h"
#include "tokens.h"
#include "keywords.h"
#include "symtab.h"

/*
 * My first simplified Pascal
 */
FILE *output;//arquivo de saida
int glabel = 1;//variavel para setar as labels da tradução para assembly.
int typegive = 0;//variavel auxiliar para fazer a comparação de tipos.
char lexemeaux[] = "";//variavel string que guarda uma palavra para ser usada mais tarde.
int countlexeme = 0;//variavel que guarda o nivel lexico.
int funcflag = 0;//flag para setar o offset de funções.
int offset = 0;//variavel que guarda o valor do offset.
int arraynum = 1;//variavel que guarda o tamanho do array.
int flagneg = 0;//variavel que guarda o operador negativo.
int flagrelop = 0;//variavel que guarda o operador relop.
int flagadd = 0;//variavel que guarda o operador de adição.
int flagmull = 0;//variavel que guarda o operador de multiplicação.

/*
 * Syntax:
 *
 * pas -> block '.'
 */

void
pas(void)
{
    {/** */fprintf(output, "#Header assembly\n");}
    blockmain();
    match('.');
    printsymtab();
    printtypetab();
//    {/** */fprintf(output, "#END assembly\n");}
    {/** */fprintf(output, "_exit:\n");}
    {/** */fprintf(output, "\tint $0x80\n");}
    {/** */fprintf(output, "\tleave\n");}
    {/** */fprintf(output, "\tret\n");}
}

/*
 * blockmain -> declscope stmtblock
 */
void
blockmain(void)
{
    //match(PROGRAM);
    //match(ID);
    //match(';');
    declscope();
    {/** */fprintf(output, "\n\t.section .bss\n");}
    {/** */fprintf(output, "\n\t.section .data\n");}
    {/** */fprintf(output, "\n\t.section .text\n");}
    {/** */fprintf(output, "\t.globl _start\n");}
    {/** */fprintf(output, "\t.type _start,@function\n\n");}
    {/** */fprintf(output, "_start: \n");}
    stmtblock();
}

/*
 * block -> declscope stmtblock
 */
void
block(void)
{
    //match(PROGRAM);
    //match(ID);
    //match(';');
    declscope();
    stmtblock();
}

/*
 * declsope -> UINT intlist ';'
 *          |  TYPE ID '=' type ';' {ID '=' type ';'}
 *	    |  CONST ID '=' constantfloat ';' {ID '=' constantfloat ';'}
 *          |  VAR idlist ':' [ARRAY '[' simpletype {',' simpletype} ']' OF] typespec ';'
 * 	    |  FUNCTION ID ['(' param ')'] ':' [ARRAY '[' simpletype {',' simpletype} ']' OF] typespec ';' block ';'
 * 	    |  PROCEDURE ID ['(' param ')'] ';' block ';'
 *	    |  £
 */
void
declscope(void)
{
	char thislexeme[] = "";
  while(lookahead == UINT || lookahead == TYPE || lookahead == CONST || lookahead == VAR || lookahead == FUNCTION || lookahead == PROCEDURE){
//    printf("%d\n",lookahead);
    switch (lookahead){
      case UINT: 
//	printf("entra inteiro\n");
	intlist();
	match(';');
	break;
      case TYPE: 
	//printf("entra type\n");
	match(TYPE);
	{strcpy(lexemeaux, lexeme);}
	match(ID);
	match('=');
	type();
	match(';');
	while (lookahead == ID){
	    {strcpy(lexemeaux, lexeme);}
	    match(ID);
	    match('=');
	    type();
	    match(';');
	}
	break;
      case CONST: 
  //      printf("entra const!\n");
	match(CONST);
        {symtab_insert(lexeme, countlexeme);
//	 printf("%s",lexeme);
	if(countlexeme == 0)
	 offset+=64;
	 offset = sttype_insert(CONST, countlexeme, offset, 1, arraynum);}
	match(ID);
	match('=');
	constantfloat();
	match(';');
	while (lookahead == ID) {
	    {symtab_insert(lexeme, countlexeme);
	    if(countlexeme == 0)
	     offset+=64;
	     offset = sttype_insert(CONST, countlexeme, offset, 1, arraynum);}
	    match(ID);
	    match('=');
	    constantfloat();
	    match(';');
	}
	break;
      case VAR:
//	printf("entra var!\n");
	match(VAR);
	idlist(/*offset,type*/);
	match(':');
	if(lookahead == ARRAY){
               match(ARRAY);
               match('[');
               simpletype();
               while(lookahead == ','){
               		match(',');
                        simpletype();
               }
               match(']');
               match(OF);
        }
	typespec();
	match(';');
	while (lookahead == ID) {
	    idlist(/*offset, type*/);
	    match(':');
	    typespec();
	    match(';');
	}
	break;
      case FUNCTION:
	match(FUNCTION);
	//printf("%d\n",countlexeme);
	{strcpy(lexemeaux, lexeme);
	 //printf("%s, %d\n", lexemeaux, countlexeme);
         /*exit(-1);*/}
	match(ID);
	{countlexeme++;}
	//printf("%d\n", countlexeme);
	if(lookahead == '('){
		match('(');
		param();
		match(')');
	}
	match(':');
        {funcflag = 1;}
	{//printf("%s, %d\n", lexemeaux, countlexeme);
         /*exit(-1);*/
	 symtab_insert(lexemeaux, countlexeme-1);}
	if(lookahead == ARRAY){
		match(ARRAY);
                match('[');
                simpletype();
                while(lookahead == ','){
                    match(',');
                    simpletype();
                }
                match(']');
                match(OF);
	}
	typespec();
	{funcflag = 0;}
	match(';');
	strcpy(thislexeme, lexemeaux);
        {/** */fprintf(output, "%s:\n", thislexeme);}
	{/** */fprintf(output, "\tenter $16,$0\n");}
	block();
	match(';');
	{localvardelete(countlexeme);
	 countlexeme--;}
	break;
      case PROCEDURE:
//	printf("entra procedure!\n");
	match(PROCEDURE);
	{offset+=32;
	 symtab_insert(lexeme, countlexeme);
	 offset = sttype_insert(PROCEDURE, countlexeme, offset, 1, arraynum);}
	strcpy(lexemeaux, lexeme);
	match(ID);
	{countlexeme++;}
	if(lookahead == '('){
		match('(');
		param();
		match(')');
	}
	match(';');
        {/** */fprintf(output, "%s:\n", lexemeaux);}
	{/** */fprintf(output, "\tenter $16,$0\n");}
	block();
	match(';');
	{localvardelete(countlexeme);
	 countlexeme--;}
	break;
      default:;	
    }
  }
}

/*
 * param -> ID ':' [ARRAY '[' simpletype {',' simpletype} ']' OF] typespec {';' param}
 * 	 |  VAR idlist ':' [ARRAY '[' simpletype {',' simpletype} ']' OF] typespec {';' param}
 *	 |  FUNCTION ID ':' [ARRAY '[' simpletype {',' simpletype} ']' OF] typespec {';' param}
 *	 |  PROCEDURE ID {';' param}
 *	 |  £
 */
void
param(void)
{
	switch(lookahead){
	case ID: idlist();
		 match(':');
		 if(lookahead == ARRAY){
                         match(ARRAY);
                         match('[');
                         simpletype();
                         while(lookahead == ','){
                            match(',');
                            simpletype();
                         }
                 match(']');
                 match(OF);
                 }
		 typespec();
		 while(lookahead == ';'){
		 match(';');
		 param();
		 }
		 break;
        case VAR:
                match(VAR);
                idlist();
                match(':');
		if(lookahead == ARRAY){
                        match(ARRAY);
                        match('[');
                        simpletype();
                        while(lookahead == ','){
                            match(',');
                            simpletype();
                         }
                match(']');
                match(OF);
                }
                typespec();
                while(lookahead == ';'){
                match(';');
                param();
                }
                break;
        case FUNCTION:
                match(FUNCTION);
                {symtab_insert(lexeme, countlexeme);}
                match(ID);
                match(':');
		if(lookahead == ARRAY){
        	        match(ARRAY);
                	match('[');
	                simpletype();
        	        while(lookahead == ','){
                	    match(',');
	                    simpletype();
               		 }
                match(']');
                match(OF);
	        }
                typespec();
                while(lookahead == ';'){
                match(';');
                param();
                }
                break;
        case PROCEDURE:
                match(PROCEDURE);
		{offset+=32;
		 symtab_insert(lexeme, countlexeme);
	         offset = sttype_insert(PROCEDURE, countlexeme, offset, 1,arraynum);}
	        match(ID);
                while(lookahead == ';'){
                match(';');
                param();
                }
                break;
	default:;
        }
}
/*
 * idlist -> ID {',' ID}
 */
void
idlist(void)
{
    {symtab_insert(lexeme, countlexeme);}
    match(ID);
    while (lookahead == ',') {
        match(',');
	{symtab_insert(lexeme, countlexeme);}
        match(ID);
    }
}

/*
 * idlistassgnmt -> ID {',' ID}
 */
void
idlistassgnmt(void)
{
    if(type_search(lexeme, countlexeme) != -1){
        match(ID);
    }else{
	printf("Line %d - ERROR: %s is an undeclared variable.\n", line, lexeme);
	exit(-1);
    }
    while (lookahead == ',') {
        match(',');
	if(type_search(lexeme, countlexeme) != -1){
	        match(ID);
		arraynum++;
	}else{
		printf("Line %d - ERROR: %s is an undeclared variable.\n", line, lexeme);
	  	exit(-1);
	}
    }
}

/*
 * intlist -> UINT {',' UINT}
 */
void
intlist(void)
{
    match(UINT);
    while (lookahead == ',') {
	match(',');
	match(UINT);
    }
}

/*
 * variable -> ID ['(' exprlist ')'] {'[' ID ']' | '^'}
 */
void
variable(void)
{
    if(symtab_search(lexeme, countlexeme) != -1){
  	match(ID);
    }else{
	printf("Line %d - ERROR: %s is an undeclared variable.\n", line, lexeme);
	exit(-1);
    }
  if (lookahead == '('){
  	match('(');
  	exprlist();
  	match(')');
  }
  while(lookahead=='['||lookahead=='^'){
  	if(lookahead == '['){
  		match('[');
		if(symtab_search(lexeme, countlexeme) != -1){
		        match(ID);
		}else{
			printf("Line %d - ERROR: %s is an undeclared variable.\n", line, lexeme);
			exit(-1);
		}
		match(']');
    	} else {
      		match('^');
    	}
  }
}

/*
 * variableassgnmt -> ID ['(' exprlist ')'] {'[' ID ']' | '^'}
 */
void
variableassgnmt(void)
{
  if(symtab_search(lexeme, countlexeme) != -1){
	if(typegive == sttype_search(lexeme, countlexeme)){
  		match(ID);
	}else{
		printf("Line %d - ERROR: %s is from a different data type of receiver variable.\n", line, lexeme);
		exit(-1);
	}
  }else{
	printf("Line %d - ERROR: %s is an undeclared variable.\n", line, lexeme);
	exit(-1);
  }
  if (lookahead == '('){
  	match('(');
  	exprlist();
  	match(')');
  }
  while(lookahead=='['||lookahead=='^'){
  	if(lookahead == '['){
  		match('[');
		if(symtab_search(lexeme, countlexeme) != -1){
                	match(ID);
		}else{
			printf("Line %d - ERROR: %s is an undeclared variable.\n", line, lexeme);
			exit(-1);
		}
      			match(']');
    	} else {
      		match('^');
	}
  }
}

/*
 * constant -> CTE
 *	    |  CHR
 *	    |  ['-'] ID
 *	    |  ['-'] UINT
 */
void
constant(int lbl)
{
   {/** */fprintf(output, "\tpopl\t%%eax\n");}
   {/** */fprintf(output, "\tcmpl\t%%eax,(%%esp)\n");}
   {/** */fprintf(output, "\tsete\t%%al\n");}
   {/** */fprintf(output, "\tjnz .L%d\n",lbl);}
   if(lookahead == CTE){
	match(CTE);
   }else if(lookahead == CHR){
        match(CHR);
   }else{
   	if(lookahead == '-')
		match('-');
	switch(lookahead){
		case ID: {symtab_insert(lexeme, countlexeme);}
			 match(ID);
			 break;
		case UINT: match(UINT);
			   break;
	}
   }
}

/*
 * constantassgnmtA -> CTE
 *		    |  CHR
 *		    |  ['-'] ID
 *		    |  ['-'] UINT
 */
void
constantassgnmtA(void)
{
//printf("entra constant.\n");
   if(lookahead == CTE){
        match(CTE);
   }else if(lookahead == CHR){
        match(CHR);
   }else{
        if(lookahead == '-')
                match('-');
        switch(lookahead){
                case ID: if(symtab_search(lexeme, countlexeme) != -1){
	                        match(ID);
			 }else{
				printf("Line %d - ERROR: %s is an undeclared variable.\n", line, lexeme);
				exit(-1);
			 }
                         break;
                case UINT: arraynum += atoi(lexeme); 
			   match(UINT);
                           break;
        }
   }
}

/*
 * constantassgnmtB -> CTE
 *                  |  CHR
 *                  |  ['-'] ID
 *                  |  ['-'] UINT
 */
void
constantassgnmtB(void)
{
//printf("entra constant.\n");
   if(lookahead == CTE){
        match(CTE);
	arraynum += 15;
   }else if(lookahead == CHR){
        match(CHR);
	arraynum += 15;
   }else{
        if(lookahead == '-')
                match('-');
        switch(lookahead){
                case ID: if(symtab_search(lexeme, countlexeme) != -1){
                                match(ID);
				arraynum += 15;
                         }else{
                                printf("Line %d - ERROR: %s is an undeclared variable.\n", line, lexeme);
                                exit(-1);
                         }
                         break;
                case UINT: arraynum = arraynum - atoi(lexeme);
			   if(arraynum<0)arraynum*=-1; 
			   match(UINT);
                           break;
        }
   }
}

/*
 * constantfloat -> CTE
 * 		 |  CHR
 *		 |  FALSE
 *		 |  TRUE
 *		 |  ['-'] ID
 *		 |  ['-'] UINT
 *		 |  ['-'] FLOAT
 *		 |  ['-'] DBLE
 */
void
constantfloat(void)
{
   if(lookahead == CTE){
        match(CTE);
   }else if(lookahead == CHR){
        match(CHR);
   }else if(lookahead == FALSE){
	match(FALSE);
   }else if(lookahead == TRUE){
	match(TRUE);	
   }else{
        if(lookahead == '-')
                match('-');
        switch(lookahead){
                case ID:if(symtab_search(lexeme, countlexeme)!=-1){
				match(ID);
			}else{
				printf("Line %d - ERROR: %s is an undeclared variable.\n", line, lexeme);
                                exit(-1);
			}
                         break;
                case UINT: match(UINT);
                           break;
		case FLOAT: match(FLOAT);
			    break;
                case DBLE: match(DBLE);
                            break;
        }
   }
}

/*
 * constantnoneg -> UINT | CTE | CHR | FLOAT | DBLE | TRUE | FALSE
 */
void
constantnoneg(void)
{
	switch(lookahead){
        case UINT: match(UINT);
	           break;
	case CTE: match(CTE);
	          break;
	case CHR: match(CHR);
                  break;
	case FLOAT: match(FLOAT);
		    break;
	case DBLE: match(DBLE);
		    break;
	case TRUE: match(TRUE);
		   break;
	case FALSE: match(FALSE);
		    break;
        }
}

/*
 * constantnonegassgnmt -> UINT | CTE | CHR | FLOAT | DBLE | TRUE | FALSE
 */
void
constantnonegassgnmt(void)
{
	switch(lookahead){
        case UINT: if(typegive == INTEGER || typegive == REAL || typegive == DOUBLE || typegive == CONST){
		   	match(UINT);
		   }else{
		//	printf("UINT: %d\n",typegive);
			printf("Line %d - ERROR: Value doesn't match with data type variable.\n",line);
			exit(-1);
		   }
	           break;
	case CTE: if(typegive == STRING || typegive == CONST){
		  	match(CTE);
		  }else{
			printf("Line %d - ERROR: Value doesn't match with data type variable.\n",line);
			exit(-1);
		  }
	          break;
	case CHR: if(typegive == STRING || typegive == CHAR || typegive == CONST){
		  	match(CHR);
		  }else{
			printf("Line %d - ERROR: Value doesn't match with data type variable.\n",line);
			exit(-1);
		  }
                  break;
	case FLOAT: if(typegive == REAL || typegive == DOUBLE || typegive == CONST){
		  	match(FLOAT);
		    }else{
			printf("Line %d - ERROR: Value doesn't match with data type variable.\n",line);
			exit(-1);
		    }
		    break;
	case DBLE: if(typegive == DOUBLE || typegive == CONST){
		  	match(DBLE);
		    }else{
			printf("Line %d - ERROR: Value doesn't match with data type variable.\n",line);
			exit(-1);
		    }
	            break;
	case TRUE: if(typegive == BOOLEAN || typegive == CONST){
		  	match(TRUE);
		   }else{
			printf("Line %d - ERROR: Value doesn't match with data type variable.\n",line);
			exit(-1);
		   }
		    break;
	case FALSE: if(typegive == BOOLEAN || typegive == CONST){
		  	match(FALSE);
		    }else{
			printf("Line %d - ERROR: Value doesn't match with data type variable.\n",line);
			exit(-1);
		    }
		    break;
        }
}

/*
 * typespec -> ID | INTEGER | REAL | DOUBLE | STRING | CHAR | BOOLEAN
 */
void
typespec(void)
{
    int varsize, aux;
    int vartype;
    switch (lookahead) {
    case ID:
	vartype = type_type_search(lexeme, countlexeme);
//	printf("%s, %d, %d\n", lexeme, type_type_search(lexeme), type_array_search(lexeme));
	if (vartype == INTEGER){varsize = 32*type_array_search(lexeme, countlexeme); aux = 1;}
	else if(vartype == REAL || vartype == DOUBLE){varsize = 64*type_array_search(lexeme, countlexeme); aux = 1;}
	else if(vartype == STRING){varsize = 8*8*type_array_search(lexeme, countlexeme); aux = 8;}
	else if(vartype == CHAR){varsize = 8*type_array_search(lexeme, countlexeme); aux = 1;}
	else{varsize = 1*type_array_search(lexeme, countlexeme); aux = 1;}
	if(countlexeme == 0 || funcflag)
	  offset+=varsize;
	offset = sttype_insert(vartype, countlexeme, offset, aux, type_array_search(lexeme, countlexeme));
	arraynum = 1;
	match(ID);
	break;
    case INTEGER:
	{if(countlexeme == 0 || funcflag)
	   offset+=32*arraynum;
	 offset = sttype_insert(INTEGER, countlexeme, offset, 1, arraynum);
	 arraynum = 1;}
        match(INTEGER);
        break;
    case REAL:
	{if(countlexeme == 0 || funcflag)
	   offset+=64*arraynum;
	 offset = sttype_insert(REAL, countlexeme, offset, 1, arraynum);
	 arraynum = 1;}
        match(REAL);
	break;
    case DOUBLE:
        {if(countlexeme == 0 || funcflag)  
 	   offset+=64*arraynum;
	 offset = sttype_insert(DOUBLE, countlexeme, offset, 1, arraynum);
	 arraynum = 1;}
	match(DOUBLE);
        break;
    case STRING:
	match(STRING);
	if(lookahead == '['){
		match('[');
		aux = atoi(lexeme);
		match(UINT);
		match(']');
		{if(countlexeme == 0 || funcflag)
		   offset+=8*aux*arraynum;
        	 offset = sttype_insert(STRING, countlexeme, offset, aux, arraynum);
		 arraynum = 1;}
	}else{
	        {if(countlexeme == 0 || funcflag)
		   offset+=8*8*arraynum;
	         offset = sttype_insert(STRING, countlexeme, offset, 8,arraynum);
		 arraynum = 1;}
	}
	break;
    case CHAR:
        {if(countlexeme == 0 || funcflag)
	   offset+=8*arraynum;
	 offset = sttype_insert(CHAR, countlexeme, offset, 1, arraynum);
	 arraynum = 1;}
	match(CHAR);
	break;
//    case BOOLEAN:
    default:
        {if(countlexeme == 0 || funcflag)
	   offset+=1*arraynum;
	 offset = sttype_insert(BOOLEAN, countlexeme, offset, 1, arraynum);
	 arraynum = 1;}
        match(BOOLEAN);
//	break;
    }
}

/*
 * typetypespec -> ID | INTEGER | REAL | DOUBLE | STRING | CHAR | BOOLEAN
 */
void
typetypespec(void)
{
    //printf("%s, %d, %d\n",lexemeaux, lookahead, arraynum);
    switch (lookahead) {
    case INTEGER:
        {type_insert(lexemeaux, INTEGER, arraynum, countlexeme);arraynum=1;}
        match(INTEGER);
        break;
    case REAL:
	{type_insert(lexemeaux, REAL, arraynum, countlexeme);arraynum=1;}
        match(REAL);
        break;
    case DOUBLE:
	{type_insert(lexemeaux, DOUBLE, arraynum, countlexeme);arraynum=1;}
        match(DOUBLE);
        break;
    case STRING:
	{type_insert(lexemeaux, STRING, arraynum, countlexeme);arraynum=1;}
        match(STRING);
        break;
    case CHAR:
	{type_insert(lexemeaux, CHAR, arraynum, countlexeme);arraynum=1;}
        match(CHAR);
        break;
//    case BOOLEAN:
    default:
	{type_insert(lexemeaux, BOOLEAN, arraynum, countlexeme);arraynum=1;}
        match(BOOLEAN);
//      break;
    }
}

/*
 * simpletype -> ID
 *	      |  '^' ID
 *	      |  '(' idlistassgnmt ')'
 *	      |  constantassgnmtA '.' '.' constantassgnmtB
 */
void
simpletype(void)
{
//      printf("entrou simpletype\n");
	switch (lookahead){
        case ID:
		if(symtab_search(lexeme, countlexeme) != -1){
		        match(ID);
			arraynum += 8;
		}else{
			printf("Line %d - ERROR: %s is an undeclared variable.\n", line, lexeme);
			exit(-1);
		}
                break;
	case '^':
		match('^');
                if(symtab_search(lexeme, countlexeme) != -1){
                        match(ID);
                        arraynum += 8;
                }else{
                        printf("Line %d - ERROR: %s is an undeclared variable.\n", line, lexeme);
                        exit(-1);
                }
                break;
	case '(': match('(');
		  idlistassgnmt(/*offset,type*/);
		  match(')');
		  break;
	default: constantassgnmtA();
	         match('.');match('.');
	         constantassgnmtB();
        }
}

/*
 * simpletypetype -> ID [VALUE constantfloat]
 *            	  |  '^' ID [VALUE constantfloat]
 *            	  |  '(' idlistassgnmt ')'
 *            	  |  constantassgnmtA '.' '.' constantassgnmtB
 */
void
simpletypetype(void)
{
//      printf("entrou simpletype\n");
        switch (lookahead){
        case ID:
                if(type_search(lexeme, countlexeme) != -1){
			{type_insert(lexemeaux,type_type_search(lexeme, countlexeme),type_array_search(lexeme, countlexeme), countlexeme);}
                        match(ID);
                }else{
                        printf("Line %d - ERROR: %s is an undeclared type variable.\n", line, lexeme);
                        exit(-1);
                }
                if(lookahead == VALUE){
                        match(VALUE);
                        constantfloat();
                }
                break;
        case '^':
                match('^');
                if(type_search(lexeme, countlexeme) != -1){
			{type_insert(lexemeaux,type_type_search(lexeme, countlexeme),type_array_search(lexeme, countlexeme), countlexeme);}
                        match(ID);
                }else{
                        printf("Line %d - ERROR: %s is an undeclared type variable.\n", line, lexeme);
                        exit(-1);
                }
                if(lookahead == VALUE){
                        match(VALUE);
                        constantfloat();
                }
                break;
        case '(': match('(');
                  idlistassgnmt(/*offset,type*/);
                  match(')');
                  break;
        default: constantassgnmtA();
                 match('.');match('.');
                 constantassgnmtB();
        }
}

/*
 * type -> ARRAY '[' simpletype {',' simpletype} ']' OF typetypespec
 *	|  FILE OF typetypespec
 *	|  RECORD ID ':' typespec ';' {ID : typespec ';'} END
 *	|  SET OF typetypespec
 *	|  INTEGER [VALUE ['-'] UINT]
 *	|  REAL [VALUE ['-'] (UINT|FLOAT)]
 *	|  DOUBLE [VALUE ['-'] (UINT|FLOAT|DBLE)]
 *	|  STRING [VALUE (CHR|CTE)]
 *	|  CHAR [VALUE CHR]
 *	|  BOOLEAN [VALUE (FALSE|TRUE)]
 *	|  simpletypetype
 */
void
type(void)
{
	switch(lookahead){
	case ARRAY: match(ARRAY);
		    match('[');
		    simpletype();
		    while(lookahead == ','){
		    	match(',');
			simpletype();
		    }
		    match(']');
		    match(OF);
		    typetypespec();
		    break;
	case FILe: match(FILe);
		   match(OF);
		   typetypespec();
		   break;
	case RECORD: match(RECORD);
//		     {symtab_insert(lexeme, countlexeme);}
		     match(ID);
		     match(':');
		     typespec();
		     match(';');
		     while(lookahead == ID){
//			{symtab_insert(lexeme, countlexeme);}
		     	match(ID);
			match(':');
			typespec();
			match(';');
		     }
		     match(END);
		     break;
	case SET: match(SET);
		  match(OF);
		  typetypespec();
		  break;
	case INTEGER:
        	  {arraynum = 1;type_insert(lexemeaux, INTEGER, arraynum, countlexeme);}
	          match(INTEGER);
		  if (lookahead == VALUE){
			match(VALUE);
			if(lookahead == '-')
				match('-');
			match(UINT);
		  }
	          break;
	case REAL:
        	  {arraynum = 1;type_insert(lexemeaux, REAL, arraynum, countlexeme);}
	          match(REAL);
		  if (lookahead == VALUE){
                        match(VALUE);
                        if(lookahead == '-')
                                match('-');
                        switch(lookahead){
				case UINT:match(UINT);
					  break;
				case FLOAT:match(FLOAT);
					   break;
			}
                  }
        	  break;
        case DOUBLE:
	          {arraynum = 1;type_insert(lexemeaux, DOUBLE, arraynum, countlexeme);}
	          match(DOUBLE);
		  if (lookahead == VALUE){
                        match(VALUE);
                        if(lookahead == '-')
                                match('-');
			switch(lookahead){
                                case UINT:match(UINT);
                                          break;
                                case FLOAT:match(FLOAT);
                                           break;
				case DBLE:match(DBLE);
					  break;
                        }
                  }
	          break;
        case STRING:
	          {arraynum = 1;type_insert(lexemeaux, STRING, arraynum, countlexeme);}
	          match(STRING);
		  if (lookahead == VALUE){
                        match(VALUE);
			switch(lookahead){
                                case CHR:match(CHR);
                                          break;
                                case CTE:match(CTE);
                                           break;
                        }

                  }
	          break;
        case CHAR:
	          {arraynum = 1;type_insert(lexemeaux, CHAR, arraynum, countlexeme);}
	          match(CHAR);
		  if (lookahead == VALUE){
                        match(VALUE);
                        match(CHR);
                  }
	          break;
        case BOOLEAN:
	          {arraynum = 1;type_insert(lexemeaux, BOOLEAN, arraynum, countlexeme);}
	          match(BOOLEAN);
		  if (lookahead == VALUE){
                        match(VALUE);
			switch(lookahead){
                                case FALSE:match(FALSE);
                                          break;
                                case TRUE:match(TRUE);
                                           break;
                        }

                  }
	          break;
	default: simpletypetype();
	}
}
/*
 * stmtblock -> BEGIN stmtlist END
 */
void
stmtblock(void)
{
    match(BEGIN);
    stmtlist();
    match(END);
}
/*
 * stmtlist -> stmt {';' stmt}
 */
void
stmtlist(void)
{
    stmt();
    while (lookahead == ';') {
        match(';');
        stmt();
    }
}
/*
 * stmt -> [UINT ':'] 
 *	 (stmtblock
 *       | ifstmt
 *       | whilestmt
 *       | repeat
 *       | forstmt
 *       | casestmt
 *	 | gotostmt
 *       | assgmnt
 *       | epsilon)
 */
void
stmt(void)
{
    if(lookahead == UINT){
	{/** */fprintf(output, ".L%s:\n",lexeme);}
	match(UINT);
	match(':');
    }
    switch (lookahead) {
    case BEGIN:
        stmtblock();
        break;
    case IF:
        ifstmt();
        break;
    case WHILE:
        whilestmt();
        break;
    case REPEAT:
        repeat();
        break;
    case FOR:
        forstmt();
        break;
    case CASE:
        casestmt();
        break;
    case GOTO:
	gotostmt();
	break;
    case ID:
        assgnmt();
        break;
    default:;
    }
}
/*
 * ifstmt -> IF expr THEN stmt [ ELSE stmt ]
 */
void
ifstmt(void)
{
    int lbl = glabel++;
	int lbl2 = lbl;
//    {/** */fprintf(output, "\n#IF\n");}
    match(IF);
    expr();//{/** */fprintf(output, "\texpr.trad\n");}
    {/** */fprintf(output, "\tjz .L%d\n", lbl);}
    match(THEN);
    stmt();//{/** */fprintf(output, "\tstmt.trad\n");}
    if (lookahead == ELSE) {
        lbl2 = glabel++;
        {/** */fprintf(output, "\tjmp .L%d\n", lbl2);}
		match(ELSE);
		{/** */fprintf(output, ".L%d:\n", lbl);}
        stmt();//{/** */fprintf(output, "\tstmt.trad\n");}
    }
	{/** */fprintf(output, ".L%d:\n", lbl2);}
}
/*I
 * whilestmt -> WHILE expr DO stmt
 */
void
whilestmt(void)
{
  int lbl = glabel++;
  int lbl2 = glabel++;
//    {/** */fprintf(output, "\n#WHILE\n");}
    match(WHILE);
    {/** */fprintf (output, ".L%d:\n", lbl);}
    expr();//{/** */fprintf(output, "\texpr.trad\n");}
    {/** */fprintf(output, "\tjz .L%d\n", lbl2);}
    match(DO);
    stmt();//{/** */fprintf(output, "\tstmt.trad\n");}
    {/** */fprintf (output, "\tjmp .L%d\n", lbl);}
    {/** */fprintf (output, ".L%d:\n", lbl2);}
    match(';');
}
/*
 * repeat -> REPEAT stmtlist UNTIL expr
 */
void
repeat(void)
{
  int lbl = glabel++;
//    {/** */fprintf(output, "\n#REPEAT\n");}
    match(REPEAT);
    {/** */fprintf(output, ".L%d:\n)", lbl);}
    stmtlist();//{/** */fprintf(output, "expr.trad");}
    match(UNTIL);
    expr();//{/** */fprintf(output, "expr.trad");}
    {/** */fprintf(output, "\njz .L%d\n", lbl);}
}
/*
 * forstmt -> FOR assgnmt (TO|DOWNTO) expr DO stmt
 */
void
forstmt(void)
{
  int lbl = glabel++;
  int lbl2 = glabel++;
  char aux[] = "";
  int flag = 0;

//    {/** */fprintf(output, "\n#FOR\n");}
    match(FOR);
    strcpy(aux,lexeme);
    assgnmt();//{/** */fprintf(output, "\tassgnmt.trad\n");}

    if (lookahead == TO){
		match(TO);
		flag = 1;
    }
    else{
        match(DOWNTO);
    }
    {/** */fprintf(output, ".L%d:\n", lbl);}
    expr();//{/** */fprintf(output, "expr.trad");}
    {/** */fprintf(output, "\tmovl\t%s, %%eax\n", aux);}
    {/** */fprintf(output, "\tcmpl\t(%%esp), %%eax\n");}
    {/** */fprintf(output, "\tjz .L%d\n", lbl2);}
    match(DO);
    stmt();//{/** */fprintf(output, "\tstmt.trad\n");}
    if(flag = 1)
    	{/** */fprintf(output, "\tincl\t%s\n",aux);}
    else
    	{/** */fprintf(output, "\tdecl\t%s\n",aux);}

    {/** */fprintf(output, "\tjmp .L%d\n", lbl);}
    {/** */fprintf(output, ".L%d:\n", lbl2);}
}
/*
 * assgnmt -> variable ASGNM exprassgnmt
 */
void
assgnmt(void)
{
    typegive = sttype_search(lexeme, countlexeme);
    //printf("assign: %d\n", typegive);
    variable();
    match(ASGNM);
    Eassgnmt();
}
/*
 * casestmt -> CASE ID [OF] constant {',' constant} ':' stmtlist {';' constant {',' constant} ':' stmtlist } [ELSE stmtlist] END
 */
void
casestmt(void)
{
    int lbl2 = glabel++;
    int lbl = glabel++;
    match(CASE);//{/** */fprintf(output, "\n#CASE\n");}
    	E();
    //{/** */fprintf(output, "\t.L%d ", lbl++);}
    match(OF);
    constant(lbl);
    while (lookahead == ',') {
	match(',');
        constant(lbl); 
    }
    {/** */fprintf(output, "\tjmp .L%d\n", lbl+1);}
    match(':');
    {/** */fprintf(output, ".L%d:\n", lbl);}
    stmt();
    {/** */fprintf(output, "\tjmp .L%d\n", lbl2);}
    while (lookahead == ';') {
	match(';');
	{/** */fprintf(output, ".L%d:\n", lbl=glabel++);}
	constant(lbl+1);
	while (lookahead == ',') {
		match(',');
        	constant(lbl+1);
    	}
	{/** */fprintf(output, "\tjmp .L%d\n", lbl+2);}
    	match(':');
	{/** */fprintf(output, ".L%d:\n", lbl=glabel++);}
	stmt();
	{/** */fprintf(output, "\tjmp .L%d\n", lbl2);}
    }
    {/** */fprintf(output, ".L%d:\n", lbl=glabel++);}
//    printf("\nteste2\n");
    {/** */fprintf(output, ".L%d:\n", lbl2);}
    if(lookahead == ELSE){
	match(ELSE);
	stmt();
    }
    match(END);
    //match(';');
}

/*
 * gotostmt -> GOTO UINT
 */
void
gotostmt(void)
{
    match(GOTO);
    {/** */fprintf(output, "\tjmp .L%s\n", lexeme);}
    match(UINT);
}

//função que aceita os operadores logicos.
isrelop(void)
{
  switch(lookahead){
    case '=': flagrelop = 1;
    return 1;
    break;

    case '<': flagrelop = 2;
    return 1;
    break;

    case '>': flagrelop = 3;
    return 1;
    break;

    case GEQ: flagrelop = 4;
    return 1;
    break;

    case LEQ: flagrelop = 5;
    return 1;
    break;

    case NEQ: flagrelop = 6;
    return 1;
    break;

    default: flagrelop = 0; return 0;
  }
}

void
printrelop(int aux)
{
	switch(aux){
	case 1:
//	    {/** */fprintf(output, "#Operation =\n");}
	    {/** */fprintf(output, "\tpopl\t%%eax\n");}
	    {/** */fprintf(output, "\tcmpl\t%%eax,(%%esp)\n");}
	    {/** */fprintf(output, "\tsete\t%%al\n");}
	    flagrelop = 0;
	break;
	case 2:
//	    {/** */fprintf(output, "#Operation <\n");}
	    {/** */fprintf(output, "\tpopl\t%%eax\n");}
	    {/** */fprintf(output, "\tcmpl\t%%eax,(%%esp)\n");}
	    {/** */fprintf(output, "\tsetl\t%%al\n");}
	    flagrelop = 0;
	break;
	case 3:
//	    {/** */fprintf(output, "#Operation >\n");}
	    {/** */fprintf(output, "\tpopl\t%%eax\n");}
	    {/** */fprintf(output, "\tcmpl\t%%eax,(%%esp)\n");}
	    {/** */fprintf(output, "\tsetg\t%%al\n");}
            flagrelop = 0;
	break;
	case 4:
//	    {/** */fprintf(output, "#Operation >=\n");}
	    {/** */fprintf(output, "\tpopl\t%%eax\n");}
	    {/** */fprintf(output, "\tcmpl\t%%eax,(%%esp)\n");}
	    {/** */fprintf(output, "\tsetge\t%%al\n");}
            flagrelop = 0;
	break;
	case 5:
//	    {/** */fprintf(output, "#Operation <=\n");}
	    {/** */fprintf(output, "\tpopl\t%%eax\n");}
	    {/** */fprintf(output, "\tcmpl\t%%eax,(%%esp)\n");}
	    {/** */fprintf(output, "\tsetle\t%%al\n");}
            flagrelop = 0;
	break;
	case 6:
//	    {/** */fprintf(output, "#Operation !=\n");}
	    {/** */fprintf(output, "\tpopl\t%%eax\n");}
	    {/** */fprintf(output, "\tcmpl\t%%eax,(%%esp)\n");}
	    {/** */fprintf(output, "\tsetne\t%%al\n");}
            flagrelop = 0;
	break;
	default:;
	}
}

//função que aceita os operadores de +
isadd(void)
{
    switch(lookahead){
      case OR: flagadd = 1;
      return 1;
      break;

      case '+': flagadd = 2;
      return 1;
      break;

      case '-': flagadd = 3;
      return 1;
      break;

      default: flagadd = 0; return 0;
    }
}

void
printadd(int aux)
{
	switch(aux){
	case 1:
//	      {/** */fprintf(output, "#Operation OR\n");}
	      {/** */fprintf(output, "\tpopl\t%%eax\n");}
	      {/** */fprintf(output, "\torl\t(%%esp), %%eax\n");}
	      {/** */fprintf(output, "\tpushl\t%%eax\n");}
	      flagadd = 0;
	break;
	case 2:
//	      {/** */fprintf(output, "#Operation +\n");}
	      {/** */fprintf(output, "\tpopl\t%%eax\n");}
	      {/** */fprintf(output, "\taddl\t(%%esp), %%eax\n");}
	      {/** */fprintf(output, "\tpushl\t%%eax\n");}
              flagadd = 0;
	break;
	case 3:
//	      {/** */fprintf(output, "#Operation -\n");}
	      {/** */fprintf(output, "\tpopl\t%%eax\n");}
	      {/** */fprintf(output, "\tsubl\t(%%esp), %%eax\n");}
	      {/** */fprintf(output, "\tpushl\t%%eax\n");}
              flagadd = 0;
	break;
	default:;
	}
}

//função que aceita os operadores de *
ismull(void)
{
  switch(lookahead){
      case AND: flagmull = 1;
      return 1;
      break;

      case '*': flagmull = 2;
      return 1;
      break;

      case '/': flagmull = 3;
      return 1;
      break;

      case DIV: flagmull = 4;
      return 1;
      break;

      case MOD: flagmull = 5;
      return 1;
      break;

      default: flagmull = 0; return 0;
    }
}

void
printmull(int aux)
{
	switch(aux){
	case 1:
//	      {/** */fprintf(output, "#Operation AND\n");}
	      {/** */fprintf(output, "\tpopl\t%%eax\n");}
	      {/** */fprintf(output, "\tandl\t(%%esp), %%eax\n");}
	      {/** */fprintf(output, "\tpushl\t%%eax\n");}
	      flagmull = 0;
	break;
	case 2:
//	      {/** */fprintf(output, "#Operation *\n");}
	      {/** */fprintf(output, "\tpopl\t%%eax\n");}
	      {/** */fprintf(output, "\timull\t(%%esp), %%eax\n");}
	      {/** */fprintf(output, "\tpushl\t%%eax\n");}
              flagmull = 0;
	break;
	case 3:
//	      {/** */fprintf(output, "#Operation /\n");}
	      {/** */fprintf(output, "\tpopl\t%%eax\n");}
	      {/** */fprintf(output, "\tcltd\n");}
	      {/** */fprintf(output, "\tidivl\t(%%esp)\n");}
	      {/** */fprintf(output, "\tpushl\t%%eax\n");}	
              flagmull = 0;
	break;
	case 4:
//	      {/** */fprintf(output, "#Operation DIV\n");}
	      {/** */fprintf(output, "\tpopl\t%%eax\n");}
	      {/** */fprintf(output, "\tidivl\t(%%esp)\n");}
	      {/** */fprintf(output, "\tpushl\t%%eax\n");}
              flagmull = 0;
	break;
	case 5:
//	      {/** */fprintf(output, "#Operation MOD\n");}
	      {/** */fprintf(output, "\tpopl\t%%eax\n");}
	      {/** */fprintf(output, "\tidivl\t(%%esp)\n");}
	      {/** */fprintf(output, "\tmovl\t%%edx, %%eax\n");}
	      {/** */fprintf(output, "\tpushl\t%%eax\n");}
              flagmull = 0;
	break;
	default:;
	}
}
/*
 * expr -> E R
 */
void
expr(void)
{
    E();
    R();
}

/*
 * R -> {relop E R}
 */
void
R(void)
{
    while (isrelop()) {
        match(lookahead);
	E();
	printrelop(flagrelop);
        R();
    }

}

/*
 * exprlist -> expr {',' expr}
 */
void
exprlist(void)
{
    expr();
    while (lookahead == ',') {
        match(',');
        expr();
    }
}

/*
 * E -> neg T {add T}
 */
void
E(void)
{
    if(isneg()){
	match(lookahead);
    }
    {/** */fprintf(output, "\tmovl %s, (%%esp)\n", lexeme);}
    T();
    printneg(flagneg);
    Q();
}

/*
 * Q -> {add T Q}
 */
void
Q(void)
{
    while (isadd()) {
        match(lookahead);
        T();
	printadd(flagadd);
	Q();
    }
}

//função que aceita o -
/*
 * neg -> '-' | NOT | epsilon
 */
isneg(void)
{
  switch(lookahead){
      case '-': flagneg = 1;
      return 1;
      break;

      case NOT: flagneg = 2;
      return 1;
      break;

      default: flagneg = 0; return 0;

  }
}

void
printneg(int aux)
{
	switch(aux){
	case 1:
//	      {/** */fprintf(output, "#Operation -\n");}
	      {/** */fprintf(output, "\tpopl\t%%eax\n");}
	      {/** */fprintf(output, "\tnotl\t%%eax\n");}
	      {/** */fprintf(output, "\tpushl\t%%eax\n");}
	      flagneg = 0;
	break;
	case 2:
//	      {/** */fprintf(output, "#Operation NOT\n");}
	      {/** */fprintf(output, "\tpopl\t%%eax\n");}
	      {/** */fprintf(output, "\tnotl\t%%eax\n");}
	      {/** */fprintf(output, "\tpushl\t%%eax\n");}
              flagneg = 0;
	break;
	default:;
	}
}

/*
 * T -> F S
 */
void











T(void)
{
    F();
    S();
}

/*
 * S -> {mull F S}
 */
void
S(void)
{
    while (ismull()) {
        match(lookahead);
        F();
	printmull(flagmull);
	flagmull = 0;
	S();
    }

}
/*
 * F -> variable
 *    | NOT F
 *    | '-' F
 *    | (expr)
 *    | constantnoneg
 */
void
F(void)
{
    switch (lookahead) {
    case ID:
        variable();
        break;
    case '(':
        match('(');
        expr();
        match(')');
	break;
    case NOT: 
	match(NOT);
	F();
	break;
    case '-':
	match('-');
	F();
	break;
    default:
	constantnoneg();
    }
}

/*
 * Eassgnmt -> neg Tassgnmt {add Tassgnmt}
 */
void
Eassgnmt(void)
{
    if(isneg()){
	match(lookahead);
    }
    {/** */
     if(typegive == INTEGER || typegive == BOOLEAN || typegive == STRING) 
     	fprintf(output, "\tmovl %s, (%%esp)\n", lexeme);
     else if(typegive == CHAR)
	fprintf(output, "\tmovl %d, (%%esp)\n", lexeme[1]);
     else if(typegive == REAL){
	char aux[10] = "";
	sprintf(aux, "%x", atoi(lexeme));
	fprintf(output, "\tmovl %s, (%%esp)\n", aux);
     }
    }
    Tassgnmt();
    printneg(flagneg);
    Qassgnmt();
}

/*
 * Q -> {add T Q}
 */
void
Qassgnmt(void)
{
    while (isadd()) {
        match(lookahead);
        Tassgnmt();
	printadd(flagadd);
        Qassgnmt();
    }
}

/*
 * Tassgnmt -> Fassgnmt { mull Fassgnmt }
 */
void
Tassgnmt(void)
{
    Fassgnmt();
    Sassgnmt();
}

void
Sassgnmt(void)
{
    while (ismull()) {
        match(lookahead);
        Fassgnmt();
	printmull(flagmull);
        Sassgnmt();
    }

}

/*
 * Fassgnmt -> variableassgnmt
 *    | NOT Fassgnmt
 *    | '-' Fassgnmt
 *    | '(' exprassgnmt ')'
 *    | constantnonegassgnmt
 */
void
Fassgnmt(void)
{
    switch (lookahead) {
    case ID:
        variableassgnmt();
        break;
    case '(':
        match('(');
        Eassgnmt();
        match(')');
	break;
    case NOT: 
	match(NOT);
	Fassgnmt();
	break;
    case '-':
	match('-');
	Fassgnmt();
	break;
    default:
	constantnonegassgnmt();
    }
}
